在我们开发过程中,相信应该有很多人使用过EventBus 3.0
,这个确实方便了我们,少些了很多代码,这是个优秀的库,我们接下来进行对他剖析。
我们使用EventBus 3.0
的过程:1
2
3EventBus.getDefault().register()
EventBus.getDefault().post()
EventBus.getDefault().unregister()
我们先看看是怎么初始化的1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38/** Convenience singleton for apps using a process-wide EventBus instance. */
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
/**
* Creates a new EventBus instance; each instance is a separate scope in which events are delivered. To use a
* central bus, consider {@link #getDefault()}.
*/
public EventBus() {
this(DEFAULT_BUILDER);
}
EventBus(EventBusBuilder builder) {
subscriptionsByEventType = new HashMap<>();
typesBySubscriber = new HashMap<>();
stickyEvents = new ConcurrentHashMap<>();
mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
backgroundPoster = new BackgroundPoster(this);
asyncPoster = new AsyncPoster(this);
indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
builder.strictMethodVerification, builder.ignoreGeneratedIndex);
logSubscriberExceptions = builder.logSubscriberExceptions;
logNoSubscriberMessages = builder.logNoSubscriberMessages;
sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
throwSubscriberException = builder.throwSubscriberException;
eventInheritance = builder.eventInheritance;
executorService = builder.executorService;
}
一个单例模式,接着点你会发现其实是利用的Builder
模式,而框架内部帮我们写了一个EventBusBuilder
1
2
3
4
5private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
EventBusBuilder() {
}
在EventBusBuilder
初始化的时候除了对成员变量的一些初始化外,其他的并没有做什么操作。
接下来我们进入register
函数1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19/**
* Registers the given subscriber to receive events. Subscribers must call {@link #unregister(Object)} once they
* are no longer interested in receiving events.
* <p/>
* Subscribers have event handling methods that must be annotated by {@link Subscribe}.
* The {@link Subscribe} annotation also allows configuration like {@link
* ThreadMode} and priority.
*/
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
//根据subscriberClass 获取订阅的方法
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
//遍历订阅
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
从这个函数我们可以很清晰的看到逻辑,首先根据subscriber(Activity,Fragment)
得到相应的订阅方法,然后在遍历订阅。这里代码就不点进去看了,主要说下框架的实现思路。
采用了缓存加反射的方式,主要的参数和类为下:METHOD_CACHE
:一个key
为订阅者class
,value
为需要订阅实现的方法的一个ConcurrentHashMap
,这个HashMap
是为了保证线程并发安全。我们获取的订阅方法就会缓存到这里面。FindState
:顾名思义,这是一个检测状态的类,里面的参数为1
2
3
4
5
6
7
8
9final List<SubscriberMethod> subscriberMethods = new ArrayList<>();
final Map<Class, Object> anyMethodByEventType = new HashMap<>();
final Map<String, Class> subscriberClassByMethodKey = new HashMap<>();
final StringBuilder methodKeyBuilder = new StringBuilder(128);
Class<?> subscriberClass;
Class<?> clazz;
boolean skipSuperClasses;
SubscriberInfo subscriberInfo;
里面比较重要的几个参数:SubscriberMethod
: 当前订阅对象(Activity,Fragment)
的订阅方法实体。1
2
3
4
5
6
7
8
9
10
11public class SubscriberMethod {
//方法
final Method method;
//模式
final ThreadMode threadMode;
//类型, 就是参数的类型 参数只能为1个
final Class<?> eventType;
final int priority;
final boolean sticky;
/** Used for efficient comparison */
String methodString;
subscriberMethods
:用来存储当前订阅对象(Activity,Fragment)
内的订阅方法SubscriberMethod
。anyMethodByEventType
: key
为参数类型(订阅类型),value
为方法的一个缓存。subscriberClassByMethodKey
:key
为 订阅方法名+">"+参数类型name
,value
为订阅方法的class
。
获取订阅方法的时候先判断缓存里面是否存在,不存在就获取FindState
实例,根据反射获取注解Subscribe(Activity,Fragment)
的方法,然后对FindState
实例进行操作(anyMethodByEventType
和subscriberClassByMethodKey
得到相应的值或添加到缓存),获取到的订阅方法集合其实就是FindState
实例里面的subscriberMethods
,最后在把获取的订阅方法集合加入缓存。
得到了订阅方法集合接下来就遍历subscriberMethods
:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
private final Map<Object, List<Class<?>>> typesBySubscriber;
private final Map<Class<?>, Object> stickyEvents;
// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class<?> eventType = subscriberMethod.eventType;
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
if (subscriptions.contains(newSubscription)) {
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);
}
}
int size = subscriptions.size();
for (int i = 0; i <= size; i++) {
if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
subscriptions.add(i, newSubscription);
break;
}
}
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
if (subscriberMethod.sticky) {
if (eventInheritance) {
// Existing sticky events of all subclasses of eventType have to be considered.
// Note: Iterating over all events may be inefficient with lots of sticky events,
// thus data structure should be changed to allow a more efficient lookup
// (e.g. an additional map storing sub classes of super classes: Class -> List<Class>).
Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
for (Map.Entry<Class<?>, Object> entry : entries) {
Class<?> candidateEventType = entry.getKey();
if (eventType.isAssignableFrom(candidateEventType)) {
Object stickyEvent = entry.getValue();
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}
订阅的时候,首先根据订阅者(Activity Fragment)
和SubscriberMethod
得到一个Subscription
对象,这个类就是一个真正执行信息传递的订阅对象。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48/*
* Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.greenrobot.eventbus;
final class Subscription {
final Object subscriber;
final SubscriberMethod subscriberMethod;
/**
* Becomes false as soon as {@link EventBus#unregister(Object)} is called, which is checked by queued event delivery
* {@link EventBus#invokeSubscriber(PendingPost)} to prevent race conditions.
*/
volatile boolean active;
Subscription(Object subscriber, SubscriberMethod subscriberMethod) {
this.subscriber = subscriber;
this.subscriberMethod = subscriberMethod;
active = true;
}
public boolean equals(Object other) {
if (other instanceof Subscription) {
Subscription otherSubscription = (Subscription) other;
return subscriber == otherSubscription.subscriber
&& subscriberMethod.equals(otherSubscription.subscriberMethod);
} else {
return false;
}
}
public int hashCode() {
return subscriber.hashCode() + subscriberMethod.methodString.hashCode();
}
}
接着把这个Subscription
对象添加CopyOnWriteArrayList
(CopyOnWriteArrayList
是一个适合用在读多,写少
的并发
应用中,它是一个线程安全的集合)然后将这个CopyOnWriteArrayList
添加到subscriptionsByEventType
里面,这个subscriptionsByEventType
是一个key
为订阅方法的类型(方法函数的类型),value
为一个存放Subscription
的CopyOnWriteArrayList
的HashMap
。接下来把订阅方法的类型(参数类型)eventType
添加到一个ArrayList
里面,在将这个ArrayList
添加到typesBySubscriber
,typesBySubscriber
是一个key
为订阅者对象(Fragment,Activity
),value
为ArrayList<Class<?>>
的HashMap
,最后在判断是否是sticky
,如果是的就将遍历一个stickyEvents
的HashMap
,然后根据key
(订阅方法类型)发出消息。
我们看看unregister
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29/** Unregisters the given subscriber from all event classes. */
public synchronized void unregister(Object subscriber) {
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
if (subscribedTypes != null) {
for (Class<?> eventType : subscribedTypes) {
unsubscribeByEventType(subscriber, eventType);
}
typesBySubscriber.remove(subscriber);
} else {
Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
/** Only updates subscriptionsByEventType, not typesBySubscriber! Caller must update typesBySubscriber. */
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
List<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions != null) {
int size = subscriptions.size();
for (int i = 0; i < size; i++) {
Subscription subscription = subscriptions.get(i);
if (subscription.subscriber == subscriber) {
subscription.active = false;
subscriptions.remove(i);
i--;
size--;
}
}
}
}
很简单,先根据订阅者(Activity Fragment)
获取到typesBySubscriber
里面的订阅方法类型集合(List<Class<?>> subscribedTypes
),然后遍历这个集合,根据这个订阅方法类型得到subscriptionsByEventType
里面的订阅对象集合List<Subscription> subscriptions
,在遍历这个subscriptions
集合,判断subscription.subscriber == subscriber
然后移除,最后在移除typesBySubscriber
里面的这个订阅对象(Fragment ,Activity )
。
接下来我们看看post
,最终会走到这个函数
1 | private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) { |
在发送消息的时候根据消息类型(参数类型)
然后从上面的subscriptionsByEventType
里面取出相应的数据进行封装成一个PendingPost
对象,在根据反射invoke
对应的方法即可,如果是MAIN
的话就通过Handler
进行线程转换,如果是BACKGROUND
并且是在主线程中调用或者是ASYNC
将会通过线程池来进行线程切换。
接下来看看postSticky
:1
2
3
4
5
6
7
8
9
10
11/**
* Posts the given event to the event bus and holds on to the event (because it is sticky). The most recent sticky
* event of an event's type is kept in memory for future access by subscribers using {@link Subscribe#sticky()}.
*/
public void postSticky(Object event) {
synchronized (stickyEvents) {
stickyEvents.put(event.getClass(), event);
}
// Should be posted after it is putted, in case the subscriber wants to remove immediately
post(event);
}
这个其实我们在注册的时候已经分析了,当调用这个函数的时候,将会将这个消息加到stickyEvents
里面,这个stickyEvents
是一个ConcurrentHashMap
,ConcurrentHashMap
是一个应用于高并发的键值对,接着调用post
函数先把已经注册的观察者的方法实现,接下来其他对象注册的时候1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19if (subscriberMethod.sticky) {
if (eventInheritance) {
// Existing sticky events of all subclasses of eventType have to be considered.
// Note: Iterating over all events may be inefficient with lots of sticky events,
// thus data structure should be changed to allow a more efficient lookup
// (e.g. an additional map storing sub classes of super classes: Class -> List<Class>).
Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
for (Map.Entry<Class<?>, Object> entry : entries) {
Class<?> candidateEventType = entry.getKey();
if (eventType.isAssignableFrom(candidateEventType)) {
Object stickyEvent = entry.getValue();
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
在这里将会调用。
还有一个优先级priority
问题,可以看到也是在注册的时候1
2
3
4
5
6
7int size = subscriptions.size();
for (int i = 0; i <= size; i++) {
if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
subscriptions.add(i, newSubscription);
break;
}
}
其实也是很简单的原理。源码分析得也差不多了,具体的需要大家自己去查看源码。
总结,EventBus
的实现方式:反射 + 数据封装 + 缓存 + 线程切换
通过查看EventBus
的源码收获了什么?
1. Builder模式封装参数 2. 根据用处不同进行对不同的类封装,比如:`SubscriberMethodFinder`用于订阅方法的查找;`FindState`一个用`SubscriberMethodFinder`查找的辅助类,里面封装了一些数据;`SubscriberMethod`订阅方法实体,就是通过注解的方法对应的对象;`Subscription`一个订阅对象。 3. 缓存 4. 线程切换使用方式 5. 反射用法熟悉加强 6. `ThreadLocal`是一个关于创建线程局部变量的类。 通常情况下,我们创建的变量是可以被任何一个线程访问并修改的。而使用`ThreadLocal`创建的变量只能被当前线程访问,其他线程则无法访问和修改。 7. `CopyOnWriteArrayList`:是一个适合用在读多,写少的并发应用中,它是一个线程安全的集合 8. `ConcurrentHashMap`:是一个应用于高并发的键值对